package com.joyoung.sweep.utils.blue;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.text.TextUtils;

import com.alibaba.fastjson.JSON;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BleLinker {

    private static final String TAG = BleLinker.class.getSimpleName();
    public static final String JOYOUNG_BLE_NAME = "JY_0015";
    private static final String BLE_SERVICE_UUID = "73657276-6963-655F-6A79-626C65000000";
    private static final String BLE_NOTIFY_CHARACTERISTIC_UUID = "72645F6E-6F74-6966-795F-6A79626C6500";
    private static final String BLE_WRITE_CHARACTERISTIC_UUID = "77745F77-6F72-7370-5F6A-79626C650000";
    private static final String TEA_ENCRYPTION_KEY = "JYBLE_Config";

    /**
     * The udp port to receive smartlink config
     */
    private static final int PORT_RECEIVE_SMART_CONFIG = 49999;
    /**
     * The udp port to send smartlinkfind broadcast
     */
    private static final int PORT_SEND_UDP_FIND = 33999;
    private static final int DEFAULT_TIMEOUT_PERIOD = 20000;
    private static final int DEFAULT_SOCKET_TIMEOUT_PERIOD = 400;

    private static final int RETRY_MAX_TIMES = 6;

    private Context context;
    private String ssid;
    private String password;
    private String bleName = JOYOUNG_BLE_NAME;

    private OnLinkListener onLinkListener;
    private WifiManager wifiManager;
    private LinkTask linkTask;
    private LinkingProgress linkingProgress;
    private int timeoutPeriod = DEFAULT_TIMEOUT_PERIOD;
    private Timer timer;
    private boolean isTimeout;
    private WifiManager.WifiLock wifiLock;

    private Ble ble;
    private BroadcastReceiver bluetoothStateChangedReceiver;
    private LinkingStatus linkingStatus = new LinkingStatus();
    private boolean bleNameStrictMatching = true;


    /**
     * The flag indicates the smartlink is working
     */
    private boolean isLinking;

    public String getSsid() {
        return ssid;
    }

    public void setSsid(String ssid) {
        this.ssid = ssid;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBleName() {
        return bleName;
    }

    public void setBleName(String bleName) {
        this.bleName = bleName;
    }

    public void setTimeoutPeriod(int timeoutPeriod) {
        this.timeoutPeriod = timeoutPeriod;
    }

    public boolean isLinking() {
        return isLinking;
    }

    public void setOnLinkListener(OnLinkListener onLinkListener) {
        this.onLinkListener = onLinkListener;

        if (onLinkListener != null) {

            try {
                onLinkListener.onBluetoothEnabledChanged(ble.isAdapterOn());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void setBleNameStrictMatching(boolean bleNameStrictMatching) {
        this.bleNameStrictMatching = bleNameStrictMatching;
    }

    private static class BleLinkerHolder {
        private static final BleLinker BLE_LINKER = new BleLinker();
    }

    private class LinkingStatus {

        private boolean canceled;
        private LinkingProgress progress;
        private Map<String, Object> data = new HashMap<>();
        private static final String KEY_SCANNED_BLE = "KEY_SCANNED_BLE";
        private static final String KEY_CONNECT_BLE = "KEY_CONNECT_BLE";
        private static final String KEY_CONFIG_BLE = "KEY_CONFIG_BLE";
        private static final String KEY_CONFIG_BLE_SUCCESS = "KEY_CONFIG_BLE_SUCCESS";

        public boolean isCanceled() {
            return canceled;
        }

        public void setCanceled(boolean canceled) {
            this.canceled = canceled;
        }

        public Map<String, Object> getData() {
            return new HashMap<>(data);
        }

        public Object getData(String key) {
            return data.get(key);
        }

        public void setData(String key, Object value) {
            data.put(key, value);
        }

        public LinkingProgress getProgress() {
            return progress;
        }

        public void setProgress(LinkingProgress progress) {
            this.progress = progress;
        }

        public String toJson() {
            return JSON.toJSONString(this);
        }

        public LinkingStatus(LinkingProgress progress) {
            this.progress = progress;
        }

        public LinkingStatus() {
        }

        public void reset() {
            LogUtil.d(TAG, "[reset] LinkingStatus");
            canceled = false;
            data.clear();
            progress = null;
        }
    }

    private BleLinker() {
        if (bluetoothStateChangedReceiver == null) {
            bluetoothStateChangedReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {

                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                    if (state == BluetoothAdapter.STATE_ON) {

                        if (onLinkListener != null) {

                            try {
                                onLinkListener.onBluetoothEnabledChanged(true);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    } else if (state == BluetoothAdapter.STATE_OFF) {

                        if (onLinkListener != null) {

                            try {
                                onLinkListener.onBluetoothEnabledChanged(false);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            };
        }
    }

    public static BleLinker getInstance(Context context) {

        BleLinker bleLinker = BleLinkerHolder.BLE_LINKER;
        if (bleLinker.context == null) {
            bleLinker.context = context;
        }

        return BleLinkerHolder.BLE_LINKER;
    }

    public WifiInfo getConnectedWifi() {
        return wifiManager.getConnectionInfo();
    }

    public void init(Context context) {
        this.context = context;
        this.context.registerReceiver(bluetoothStateChangedReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
        ble = Ble.getInstance(context);
        ble.setCallback(new BleCallback() {

            @Override
            public void onDeviceFind(BluetoothDevice device, int rssi, byte[] scanRecord) {
                super.onDeviceFind(device, rssi, scanRecord);
                String deviceName = device.getName();
                if (!TextUtils.isEmpty(deviceName) && ((bleNameStrictMatching && deviceName.equals(bleName)) || (!bleNameStrictMatching && deviceName.contains(bleName)))) {

                    synchronized (linkingStatus) {
                        linkingStatus.setData(LinkingStatus.KEY_SCANNED_BLE, device);
                    }
                    ble.stopScanDevice();
                }
            }

            @Override
            public void onConnectionChanged(int state, int status) {
                LogUtil.e(TAG, "BleCallback.onConnectionChanged state:" + state + " status:" + status);

                if (BleCallback.STATE_CONNECTED == state) {
                    LogUtil.d(TAG, "ble connection is created and enable notify");
                    ble.enableNotify(true);
                } else if (BleCallback.STATE_DISCONNECTED == state) {
                    LogUtil.d(TAG, "ble connection is disconnected!");

                    Boolean configSuccess = (Boolean) linkingStatus.getData(LinkingStatus.KEY_CONFIG_BLE_SUCCESS);
                    if (configSuccess) {
                        // status!=0为异常状态，重新开始蓝牙流程
                        //todo 暂时注销，看测试效果 lixuecheng 2019/12/25
                        // 异常重连机制
                        if (status != 0 && !isLinking) {
                            ble.stopScanDevice();
                            ble.close();
                            synchronized (linkingStatus) {
                                linkingStatus.setCanceled(true);
                                linkingStatus.reset();
                                linkingStatus.notifyAll();
                            }
                            if (linkTask != null) {
                                linkTask.cancel(true);
                            }
                            LogUtil.i(TAG, "开始新的BLE大流程");
                            linkTask = new LinkTask();
                            ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(2);
                            linkTask.executeOnExecutor(mFixedThreadPool);
                        }
                    }
                }
            }

            @Override
            public void onDataNotified(byte[] data) {
                super.onDataNotified(data);
                String text = new String(data);
                LogUtil.w(TAG, String.format("BleCallback.onDataNotified: hex-%s text-'%s'", LinkerUtils.bytes2HexStringWithWhitespace(data), text));

                if (data[0] == (byte) 0xB0) { // 可以进行更完整解析校验，但是不一定会回复这条消息
                    int size = data.length;
                    byte checkbit = PackUtil.bytesAnd(data, 0, size - 1);
                    if (checkbit == data[size - 1]) {
                        LogUtil.w(TAG, "BleCallback.onDataNotified: 校验成功");
                        analysisReceiveCmd(data);
                        analysisAckCmd(data);
                    }
                }
            }

            @Override
            public void onDataWritten(byte[] data, boolean success) {
                super.onDataWritten(data, success);
                LogUtil.w(TAG, String.format("BleCallback.onDataWritten: data-%s success-%s", LinkerUtils.bytes2HexStringWithWhitespace(data), success));

                if (success) {
                    synchronized (linkingStatus) {
                        linkingStatus.setData(LinkingStatus.KEY_CONFIG_BLE, data);
                        linkingStatus.notifyAll();
                    }
                }
            }

            @Override
            public void onNotifyChanged(Boolean enabled) {
                super.onNotifyChanged(enabled);
                LogUtil.d(TAG, "BleCallback.onNotifyChanged: " + JSON.toJSONString(enabled));

                if (enabled) {
                    synchronized (linkingStatus) {
                        linkingStatus.setData(LinkingStatus.KEY_CONNECT_BLE, true);
                        linkingStatus.notifyAll();
                    }
                }
            }

            @Override
            public void onScanFinished() {
                super.onScanFinished();
                LogUtil.d(TAG, "BleCallback.onScanFinished");

                synchronized (linkingStatus) {
                    linkingStatus.notifyAll();
                }
            }
        });

        this.resetProperties();
    }

    public void destroy() {
        try {
            context.unregisterReceiver(bluetoothStateChangedReceiver);
        } catch (Exception e) {
        }
        LogUtil.e(TAG, "---------------->>>destroy(): ");
        this.stop();
        if (linkTask != null) {
            linkTask.cancel(true);
        }
        this.resetProperties();
    }

    public void start() throws Exception {
        LogUtil.d(TAG, "=========start start==============");
        if (TextUtils.isEmpty(ssid)) {
            throw new Exception("ssid is empty");
        }
        if (ssid.length() > PackUtil.S_SSID_MAX_LENGTH) {
            throw new Exception("ssid's length is large than " + PackUtil.S_SSID_MAX_LENGTH);
        }
        if (!TextUtils.isEmpty(password) && password.length() > PackUtil.S_PASSWORD_MAX_LENGTH) {
            throw new Exception("Wi-Fi password's length is large than " + PackUtil.S_PASSWORD_MAX_LENGTH);
        }

        if (TextUtils.isEmpty(bleName)) {
            throw new Exception("bleName is empty");
        }

        if (isLinking) {
            return;
        }

        resetLinkProperties();
        isLinking = true;
        ble.setServiceUuid(BLE_SERVICE_UUID);
        ble.setNotifyCharacteristicUuid(BLE_NOTIFY_CHARACTERISTIC_UUID);
        ble.setReadWriteCharacteristicUuid(BLE_WRITE_CHARACTERISTIC_UUID);
        linkingStatus.reset();

        ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(2);
        linkTask = new LinkTask();
        linkTask.executeOnExecutor(mFixedThreadPool);

        isTimeout = false;
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                LogUtil.e(TAG, "time out!");
                isTimeout = true;
                stop();
            }
        }, timeoutPeriod);
        LogUtil.d(TAG, "=========start end==============");
    }

    public void stop() {
        LogUtil.e(TAG, "---------------->>>stop: ");
        isLinking = false;
        if (timer != null) {
            try {
                timer.cancel();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        synchronized (linkingStatus) {
            linkingStatus.setCanceled(true);
            linkingStatus.notifyAll();
        }
        if (ble != null) {
            ble.stopScanDevice();
            ble.close();
        }
    }

    public boolean isBluetoothAdapterEnabled() {
        return ble.isAdapterOn();
    }

    public boolean isBleSupported() {
        return Ble.isBleSupported(context);
    }

    public void requestEnableBluetoothAdapter() {
        Ble.requestEnableBluetoothAdapter(context);
    }

    private void resetProperties() {
        ssid = null;
        password = null;
        bleName = null;
        onLinkListener = null;
        bleNameStrictMatching = true;
        this.resetLinkProperties();
    }

    private void resetLinkProperties() {
        LogUtil.d(TAG, "resetLinkProperties: ");
        isLinking = false;
        isTimeout = false;
        linkTask = null;
        linkingProgress = null;
        timer = null;
        ackDataReset();
        receiveDataReset();
    }

    private class LinkTask extends AsyncTask<Void, LinkingProgress, LinkingError> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(LinkingError error) {
            LogUtil.d(TAG, "onPostExecute: " + error);
            if (onLinkListener != null) {
                if (error == null) {
                    if (isTimeout) {
                        try {
                            onLinkListener.onTimeOut();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    try {
                        onLinkListener.onError(error);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (onLinkListener != null) {
                    try {
                        onLinkListener.onFinished("BLE 结束");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            resetLinkProperties();
            linkTask = null;
        }

        @Override
        protected void onProgressUpdate(LinkingProgress... values) {
            LogUtil.i(TAG, "LinkTask->scanBle: 3");
            linkingProgress = values[0];
            if (onLinkListener != null) {
                try {
                    onLinkListener.onProgress(linkingProgress);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        protected LinkingError doInBackground(Void... voids) {
            LogUtil.i(TAG, "LinkTask->scanBle: 1");
            if (!ble.isAdapterOn()) {
                return LinkingError.BLUETOOTH_DISABLED;
            }


            try {
                LogUtil.i(TAG, "LinkTask->scanBle: 2");
                publishProgress(LinkingProgress.SCAN_BLE);
                BluetoothDevice scannedBleDevice = scanBle();
//                LogUtil.i(TAG, "LinkTask->scanBle: " + JSON.toJSONString(scannedBleDevice));
                if (scannedBleDevice == null) {
                    return LinkingError.BLE_NOT_FOUND;
                }

                publishProgress(LinkingProgress.CONNECT_BLE);
                if (!connectBle(scannedBleDevice.getAddress())) {
                    LogUtil.w(TAG, String.format("LinkTask->connect ble device mac-%s failed", scannedBleDevice.getAddress()));
                    return LinkingError.CONNECT_BLE_FAILED;
                }
                LogUtil.i(TAG, String.format("LinkTask->connect ble device mac-%s succeed", scannedBleDevice.getAddress()));

                publishProgress(LinkingProgress.CONFIG_BLE);
                if (!configBle()) {
                    LogUtil.w(TAG, String.format("LinkTask->config ble device mac-%s failed", scannedBleDevice.getAddress()));
                    return LinkingError.CONFIG_BLE_FAILED;
                }
                LogUtil.i(TAG, String.format("LinkTask->config ble device mac-%s succeed", scannedBleDevice.getAddress()));
            } catch (LinkingCancelledException e) {
                LogUtil.w(TAG, "ap link task is canceled" + e.getMessage());
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {

            }

            return null;
        }
    }


    /**
     * Scan ble device with {@link #bleName} in 60 seconds
     */
    private BluetoothDevice scanBle() throws LinkingCancelledException {
        linkingStatus.setProgress(LinkingProgress.SCAN_BLE);
        LogUtil.w(TAG, "=======scanBle============");
        for (int i = 0; i < RETRY_MAX_TIMES; i++) {
            boolean succeed = ble.scanDevice();
            LogUtil.e(TAG, String.format("--->>>start scan ble device with name '%s' NO.%s time %s", bleName, i + 1, succeed ? "succeed" : "failed"));

            synchronized (linkingStatus) {
                try {
                    linkingStatus.wait(Ble.BLE_DISCOVERY_PERIOD);
                } catch (InterruptedException e) {
//                    e.printStackTrace();
                }

                if (linkingStatus.isCanceled()) {
                    LogUtil.e(TAG, "------->>>scanBle");
                    throw new LinkingCancelledException();
                }

                if (linkingStatus.getData(LinkingStatus.KEY_SCANNED_BLE) instanceof BluetoothDevice) {
                    return (BluetoothDevice) linkingStatus.getData(LinkingStatus.KEY_SCANNED_BLE);
                }
            }
        }

        return null;
    }

    /**
     * connect the scanned ap in 60 seconds
     */
    private boolean connectBle(String mac) throws LinkingCancelledException {

        linkingStatus.setProgress(LinkingProgress.CONNECT_BLE);

        for (int i = 0; i < RETRY_MAX_TIMES * 2; i++) {
            LogUtil.e(TAG, String.format("start to connect ble device NO.%s time", i + 1));
            boolean succeed = ble.connectDevice(mac);
            LogUtil.e(TAG, String.format("connect ble device NO.%s time %s", i + 1, succeed ? "succeed" : "failed"));

            synchronized (linkingStatus) {

                try {
                    linkingStatus.wait(3000);
                    LogUtil.e(TAG, "linkingStatus------>>>释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (linkingStatus.isCanceled()) {
                    throw new LinkingCancelledException();
                }

                if (Boolean.TRUE == linkingStatus.getData(LinkingStatus.KEY_CONNECT_BLE)) {
                    if (timer != null) {
                        LogUtil.e(TAG, "连接成功 取消定时");
                        timer.cancel();
                    }
                    return true;
                }
            }
        }

        return false;
    }

    private boolean configBle() throws LinkingCancelledException {
        linkingStatus.setProgress(LinkingProgress.CONFIG_BLE);

        List<byte[]> frames = new ArrayList<>();
        try {
            frames.addAll(this.getConfigFrames());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (frames.isEmpty()) {
            return false;
        }

        boolean[] framesWritten = new boolean[frames.size()];
        boolean result = false;

        int count = frames.size() * RETRY_MAX_TIMES * 2;
        int index = 0;
        long time = 0;
        for (int i = 0; i < count; i++) {

            if (this.isAllFramesWritten(framesWritten)) {
                LogUtil.i(TAG, "All frames is written! time is :" + System.currentTimeMillis());
                synchronized (linkingStatus) {

                    try {
                        linkingStatus.wait(1000);
                    } catch (InterruptedException e) {
//                        e.printStackTrace();
                    }

                    if (linkingStatus.isCanceled()) {
                        throw new LinkingCancelledException();
                    }

                    // 暂不会回复是否成功，继续下一轮发送，只到连接被动断开
                    Boolean configSuccess = (Boolean) linkingStatus.getData(LinkingStatus.KEY_CONFIG_BLE_SUCCESS);
                    LogUtil.d(TAG, String.format("LinkingStatus.KEY_CONFIG_BLE_SUCCESS: %s", configSuccess));
                    if (configSuccess) {
                        result = true;
                        break;
                    } else if (configSuccess) {
                        break;
                    }
                }
            }
            LogUtil.i(TAG, "time is :" + System.currentTimeMillis());
            linkingStatus.setData(LinkingStatus.KEY_CONFIG_BLE, null);
            byte[] frame = frames.get(index);
            LogUtil.i(TAG, String.format("write config data, frame[%s]-%s", index + 1,
                    LinkerUtils.bytes2HexStringWithWhitespace(frame)));
            ble.write(frame);
            time = System.currentTimeMillis();

            synchronized (linkingStatus) {
                try {
                    linkingStatus.wait(1000);
                } catch (InterruptedException e) {

                }

                if (linkingStatus.isCanceled()) {
                    LogUtil.e(TAG, "------->>>scanBle----2");
                    throw new LinkingCancelledException();
                }

                byte[] writtenData = (byte[]) linkingStatus.getData(LinkingStatus.KEY_CONFIG_BLE);
                LogUtil.d(TAG, String.format("LinkingStatus.KEY_CONFIG_BLE: %s",
                        LinkerUtils.bytes2HexStringWithWhitespace(writtenData)));

                if (!framesWritten[index] && Arrays.equals(writtenData, frame)) {
                    framesWritten[index] = true;
                }
            }

            if (framesWritten[index]) {
                index++;
            }
            if (index >= frames.size()) {
                index = 0;
            }

            if (!this.isAllFramesWritten(framesWritten)) {

                int timeInterval = 450;
                time = System.currentTimeMillis() - time;
                if (time < timeInterval) {
                    sleep(timeInterval - time);
                }
            }
        }

        if (!result) {
            LogUtil.d(TAG, "send blelink data to device failed");
            return false;
        }

        LogUtil.d(TAG, "send blelink data to device succeed");
        return true;
    }


    private byte[] getBytes(String text) {
        return TextUtils.isEmpty(text) ? new byte[0] : text.getBytes();
    }

    private void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private List<byte[]> getConfigFrames() throws Exception {
        // 蓝牙协议相关长度
        final int BLE_FRAME_MAX_LENGTH = 20;
        final int BLE_HEADER_LENGTH = 6;
        final int BLE_CRC_SUM_LENGTH = 1;
        final int BLE_FRAME_DATA_MAX_LENGTH = (BLE_FRAME_MAX_LENGTH - BLE_HEADER_LENGTH - BLE_CRC_SUM_LENGTH);
        Random random = new Random();
        int seq = random.nextInt(0xFF);

        // XXTea加密
        byte[] data = XXTea.encrypt(PackUtil.getSerialPack(ssid, password), TEA_ENCRYPTION_KEY);
        int dataLength = data.length;
        int frameCount = dataLength / BLE_FRAME_DATA_MAX_LENGTH;
        if (dataLength % BLE_FRAME_DATA_MAX_LENGTH != 0) {
            frameCount++;
        }
        int position = 0;
        List<byte[]> frames = new ArrayList<>();
        for (int i = 0; i < frameCount; i++) {
            // 当前帧dataEncryptd长度
            int bleFrameDataLength = Math.min(dataLength - position, BLE_FRAME_DATA_MAX_LENGTH);
            byte[] frame = new byte[bleFrameDataLength + BLE_HEADER_LENGTH + BLE_CRC_SUM_LENGTH];
            // 包头 APP->设备：0xB0; 设备->APP：0xA0
            frame[0] = (byte) (0xA0 & 0xFF);
            // 当前帧数据长度: 从数据长度字节下一字节开始计算，不包括校验和字节，数据长度不允许超过17字节。
            frame[1] = (byte) ((bleFrameDataLength + (BLE_HEADER_LENGTH - 2)) & 0xFF);
            // 协议说明 0x10:不需要回复，0x11需要回复
            frame[2] = (byte) (0x00);
            // 指令序号 不同指令从0x00~0xFF循环递增，这里只有一个指令
            frame[3] = (byte) (seq & 0xFF);
            // 总包数
            frame[4] = (byte) (frameCount & 0xFF);
            // 包序号 1~255
            frame[5] = (byte) ((i + 1) & 0xFF);
            System.arraycopy(data, position, frame, BLE_HEADER_LENGTH, bleFrameDataLength);
            frame[frame.length - 1] = PackUtil.bytesAnd(frame, 0, frame.length);
            frames.add(frame);

            LogUtil.d(TAG, String.format("getConfigFrames: NO.%s->%s", i + 1, LinkerUtils.bytes2HexStringWithWhitespace(frame)));

            position += bleFrameDataLength;
        }

        return frames;
    }

    private boolean isAllFramesWritten(boolean[] framesWritten) {

        for (Boolean written : framesWritten) {
            if (!written) {
                return false;
            }
        }
        return true;
    }

    private byte order = 0xFFFFFFFF;
    private List<byte[]> ack_frames = new ArrayList<>();
    private int index = 1;

    private byte receive_order = 0xFFFFFFFF;
    private List<byte[]> receive_frames = new ArrayList<>();
    private int receive_index = 1;

    private synchronized void analysisAckCmd(byte[] data) {
        int count_frame = data[4] & 0xff;
        int ack_flag = data[2] & 0xff;
        int ack_index = data[5] & 0xff;
        if (count_frame == 0) {
            T.show(this.context, "收到异常指令");
            return;
        }

        if (ack_flag == 0) {
            return;
        }
        if (order != 0xFFFFFFFF) {
            if (order == data[3] && ack_index == index) {
                index++;
                ack_frames.add(data);
                int buffer_size = ack_frames.size();
                if (ack_index == count_frame && buffer_size == count_frame) {
                    //应答--->>>复位全局变量
                    ackDataReset();
//                        List<byte[]> app_frames = new ArrayList<>();
                    writeAckData(data);

                }
            } else if (order != data[3] && ack_index == 1) {
                //重新等待新指令
                order = data[3];
                if (ack_frames != null) {
                    ack_frames.clear();
                } else {
                    ack_frames = new ArrayList<>();
                }
                index = 2;
                ack_frames.add(data);
                int buffer_size = ack_frames.size();
                if (ack_index == count_frame && buffer_size == count_frame) {
                    ackDataReset();
                    writeAckData(data);

                }
            }
        } else {
            order = data[3];
            index++;
            ack_frames.add(data);
            int buffer_size = ack_frames.size();
            if (ack_index == count_frame && buffer_size == count_frame) {
                ackDataReset();
                writeAckData(data);
            }
        }
    }

    private synchronized void analysisReceiveCmd(byte[] data) {
        int count_frame = data[4] & 0xff;
//        int receive_flag = data[2] & 0xff;
        int cmd_index = data[5] & 0xff;
        int receive_length = data[1] & 0xff;
        if (count_frame == 0) {
            T.show(this.context, "收到异常指令");
            return;
        }
        //确保data[7]不被null
        LogUtil.e(TAG, "begin receive_order is " + receive_order + ", receive_index: " + receive_index);
        if (receive_order != 0xFFFFFFFF) {
            if (receive_order == data[3] && receive_index == cmd_index) {
                int data_length = data.length;
                LogUtil.e(TAG, "i am here receive_index : " + receive_index);
                if ((data_length - 7) > 0) {
                    receive_index++;
                    byte[] subarray = null;
                    if (cmd_index != count_frame) {
                        subarray = Arrays.copyOfRange(data, 6, data.length - 1);
                    } else {
                        subarray = Arrays.copyOfRange(data, 6, data.length - 2);
                    }
                    receive_frames.add(subarray);
                    LogUtil.e(TAG, "add receive_frames : " + receive_index + ", subarray is :"
                            + LinkerUtils.bytes2HexStringWithWhitespace(subarray) + ", receive_frames size is :" + receive_frames.size());
                    if (cmd_index == count_frame) {
                        if (onLinkListener != null) {
                            byte[] allbyte = concat(receive_frames);
                            String sncode = new String(allbyte, Charset.forName("utf-8"));
                            LogUtil.e(TAG, "解析出的数据是：" + LinkerUtils.bytes2HexStringWithWhitespace(allbyte) +
                                    ", 转化字符串是：" + sncode);
                            onLinkListener.onLinked(sncode);
                        }
                        synchronized (linkingStatus) {
                            linkingStatus.setData(LinkingStatus.KEY_CONFIG_BLE_SUCCESS, true);
                            linkingStatus.notifyAll();
                        }
                        receiveDataReset();
                    }
                }
            } else if (receive_order != data[3] && receive_index == 1) {
                LogUtil.e(TAG, "进来了 is " + receive_order);
                if (ack_frames != null) {
                    ack_frames.clear();
                } else {
                    ack_frames = new ArrayList<>();
                }
                if (receive_length > 7) {
                    byte header_1 = (byte) (0xFA & 0xff);
                    byte header_2 = (byte) (0xFB & 0xff);
                    if (data[6] == header_1 && data[7] == header_2) {
                        if (receive_length - 10 > 1) {
                            byte cmd_type_first = (byte) (0xDE & 0xff);
                            byte cmd_type_second = (byte) (0x01 & 0xff);
                            if (data[12] == cmd_type_first && data[13] == cmd_type_second) {
                                if (data[16] == (byte) 0x00) {
                                    receive_order = data[3];
                                    byte[] subarray = Arrays.copyOfRange(data, receive_length, data.length - 1);
                                    receive_frames.add(subarray);
                                    receive_index = 2;
                                    LogUtil.e(TAG, "重新 第二次 i am is " + LinkerUtils.bytes2HexStringWithWhitespace(subarray));
                                }
                            }
                        }
                    }
                }
                int buffer_size = ack_frames.size();
                if (cmd_index == count_frame && buffer_size == count_frame) {
                    //todo 发送数据，解析数据
                    receiveDataReset();
                }
            }
        } else {
            if (receive_length > 7) {
                byte header_1 = (byte) (0xFA & 0xff);
                byte header_2 = (byte) (0xFB & 0xff);
                if (data[6] == header_1 && data[7] == header_2) {
                    if (receive_length - 10 > 1) {
                        byte cmd_type_first = (byte) (0xDE & 0xff);
                        byte cmd_type_second = (byte) (0x01 & 0xff);
                        if (data[12] == cmd_type_first && data[13] == cmd_type_second) {
                            if (data[16] == (byte) 0x00) {
                                receive_order = data[3];
                                LogUtil.e(TAG, "receive_order is " + receive_order);
                                byte[] subarray = Arrays.copyOfRange(data, receive_length, data.length - 1);
                                receive_frames.add(subarray);
                                receive_index++;
                                LogUtil.e(TAG, "第一次 i am is " + LinkerUtils.bytes2HexStringWithWhitespace(subarray));
                            }
                        }
                    }
                }
            }
            int buffer_size = receive_frames.size();
            if (cmd_index == count_frame && buffer_size == count_frame) {
                //todo 发送数据，解析数据
                receiveDataReset();
            }
        }
    }

    public byte[] concat(List<byte[]> lists) {
        int length = 0;
        for (byte[] array : lists) {
            length += array.length;
        }
        byte[] result = new byte[length];
        int pos = 0;
        for (byte[] array : lists) {
            System.arraycopy(array, 0, result, pos, array.length);
            pos += array.length;
        }
        return result;
    }

    private void writeAckData(byte[] data) {
        byte[] app_ack_frame = new byte[7];
        app_ack_frame[0] = (byte) (0xA0 & 0xFF);
        app_ack_frame[1] = (byte) (0x04 & 0xFF);
        app_ack_frame[2] = (byte) (0x00);
        app_ack_frame[3] = data[3];
        app_ack_frame[4] = data[4];
        app_ack_frame[5] = (byte) (0x00);
        app_ack_frame[app_ack_frame.length - 1] = PackUtil.bytesAnd(app_ack_frame, 0, app_ack_frame.length);
        LogUtil.e(TAG, String.format("app ack data is : %s",
                LinkerUtils.bytes2HexStringWithWhitespace(app_ack_frame)));
        //--->>>发送应答数据
        if (ble != null) {
            ble.write(app_ack_frame);
        } else {
            T.show(context, "蓝牙系统出现异常，请重试");
        }
    }

    private void ackDataReset() {
        if (ack_frames != null) {
            ack_frames.clear();
        } else {
            ack_frames = new ArrayList<>();
        }
        order = 0xFFFFFFFF;
        index = 1;
    }

    private void receiveDataReset() {
        LogUtil.e(TAG, "---------------->>>>receiveDataReset");
        if (receive_frames != null) {
            receive_frames.clear();
        } else {
            receive_frames = new ArrayList<>();
        }
        receive_order = 0xFFFFFFFF;
        receive_index = 1;
    }
}
